#include "visbaseelement.h"

#include "visbasewidget.h"
#include "SageVis/sagevis.h"
#include "visbasecontainer.h"

VisBaseElement::VisBaseElement(QPointer<SageVis> sageVisPointer, QPointer<VisBaseWidget> parentWidget, QGraphicsItem *parent ) :
    sageVisPointer( sageVisPointer ), parentWidget(parentWidget), QGraphicsWidget(parent)
{
    acceptHeightChanged = false;
    acceptWidthChanged = false;
    acceptXPosChanged = false;
    acceptYPosChanged = false;
    acceptHoverSignals = false;
    acceptUpdatedSignals = false;
    acceptColorChangedSignals = false;

    emitGeometryChangedSignal = true; //this is true, unless it has just recieved a signal
    emitWidthChangedSignal = true;
    emitHeightChangedSignal = true;
    emitXPosChangedSignal = true;
    emitYPosChangedSignal = true;
    emitHoverSignal = true;
    emitUpdatedSignal = true;
    emitColorChangedSignal = true;

    hovering = false;

    lineConnectionType = CENTER_LINE_CONNECTION;//default

    setGeometry(0.0, 0.0, 0.0, 0.0);//ensure that elt has some default geometry.  Needs to be changed later.
}

//----------------------------CONNECTION FUNCTIONS--------------------------
void VisBaseElement::forgeConnection(QPointer<VisBaseElement> e, int type)
{
    if( type == HEIGHT_CONNECTION )
    {
        connect( this, SIGNAL(heightChangedSignal(float)), e, SLOT(heightChangedSlot(float)) );
        acceptHeightChanged = true;
    }
    else if( type == WIDTH_CONNECTION )
    {
        connect( this, SIGNAL(widthChangedSignal(float)), e, SLOT(widthChangedSlot(float)) );
        acceptWidthChanged = true;
    }
    else if( type == XPOS_CONNECTION )
    {
        connect( this, SIGNAL(xPosChangedSignal(float)), e, SLOT(xPosChangedSlot(float)) );
        acceptXPosChanged = true;
    }
    else if( type == YPOS_CONNECTION )
    {
        connect( this, SIGNAL(yPosChangedSignal(float)), e, SLOT(yPosChangedSlot(float)) );
        acceptYPosChanged = true;
    }
    else if( type == HOVER_CONNECTION )
    {
        acceptHoverSignals = true;
        setAcceptHoverEvents(true);
        connect( this, SIGNAL(hoverConnectionSignal(QGraphicsSceneHoverEvent *)), e, SLOT(hoverConnectionSlot(QGraphicsSceneHoverEvent *)) );
    }
    else if( type == UPDATE_CONNECTION )
    {
        acceptUpdatedSignals = true;
        connect( this, SIGNAL(updatedSignal()), e, SLOT(updatedSlot()) );
    }
    else if( type == COLOR_CONNECTION )
    {
        acceptColorChangedSignals = true;
        connect(this, SIGNAL(colorChangedSignal(QColor)), e, SLOT(colorChangedSlot(QColor)) );
    }
}

void VisBaseElement::forgeConnection(QPointer<VisBaseElement> e1, QPointer<VisBaseElement> e2, int type)
{
    if( type == HEIGHT_CONNECTION )
    {
        connect( e1, SIGNAL(heightChangedSignal(float)), e2, SLOT(heightChangedSlot(float)) );
        connect( e2, SIGNAL(heightChangedSignal(float)), e1, SLOT(heightChangedSlot(float)) );
        e1->acceptHeightChanged = true;
        e2->acceptHeightChanged = true;
    }
    else if( type == WIDTH_CONNECTION )
    {
        connect( e1, SIGNAL(widthChangedSignal(float)), e2, SLOT(widthChangedSlot(float)) );
        connect( e2, SIGNAL(widthChangedSignal(float)), e1, SLOT(widthChangedSlot(float)) );
        e1->acceptWidthChanged = true;
        e2->acceptWidthChanged = true;
    }
    else if( type == XPOS_CONNECTION )
    {
        connect( e1, SIGNAL(xPosChangedSignal(float)), e2, SLOT(xPosChangedSlot(float)) );
        connect( e2, SIGNAL(xPosChangedSignal(float)), e1, SLOT(xPosChangedSlot(float)) );
        e1->acceptXPosChanged = true;
        e2->acceptXPosChanged = true;
    }
    else if( type == YPOS_CONNECTION )
    {
        connect( e1, SIGNAL(yPosChangedSignal(float)), e2, SLOT(yPosChangedSlot(float)) );
        connect( e2, SIGNAL(yPosChangedSignal(float)), e1, SLOT(yPosChangedSlot(float)) );
        e1->acceptYPosChanged= true;
        e2->acceptYPosChanged = true;
    }
    else if( type == HOVER_CONNECTION )
    {
        e1->acceptHoverSignals = true;
        e1->setAcceptHoverEvents(true);
        e2->acceptHoverSignals = true;
        e2->setAcceptHoverEvents(true);
        connect( e1, SIGNAL(hoverConnectionSignal(QGraphicsSceneHoverEvent *)), e2, SLOT(hoverConnectionSlot(QGraphicsSceneHoverEvent *)) );
        connect( e2, SIGNAL(hoverConnectionSignal(QGraphicsSceneHoverEvent *)), e1, SLOT(hoverConnectionSlot(QGraphicsSceneHoverEvent *)) );
    }
    else if( type == UPDATE_CONNECTION )
    {
        e1->acceptUpdatedSignals = true;
        e2->acceptUpdatedSignals = true;
        connect( e1, SIGNAL(updatedSignal()), e2, SLOT(updatedSlot()) );
        connect( e2, SIGNAL(updatedSignal()), e1, SLOT(updatedSlot()) );
    }
    else if( type == COLOR_CONNECTION )
    {
        e1->acceptColorChangedSignals = true;
        e2->acceptColorChangedSignals = true;
        connect(e1, SIGNAL(colorChangedSignal(QColor)), e2, SLOT(colorChangedSlot(QColor)) );
        connect(e2, SIGNAL(colorChangedSignal(QColor)), e1, SLOT(colorChangedSlot(QColor)) );
    }
}

//----------------------------SLOTS-----------------------------------------
void VisBaseElement::geometryChangedSlot(const QRectF &geom)
{
    if( !acceptXPosChanged && !acceptYPosChanged && !acceptWidthChanged && !acceptHeightChanged )
        return;
    if( (geom.x() == geometry().x()) && (geom.y() == geometry().y()) && (geom.width() == geometry().width()) && (geom.height() == geometry().height()) )
        return;

    //make sure we don't get infinite signals of geometry changed
    emitGeometryChangedSignal = false;

    //figure out what geometry changes to accept
    float x = geometry().x();
    float y =  geometry().y();
    float w = geometry().width();
    float h = geometry().height();

    if( acceptXPosChanged )
        x = geom.x();
    if( acceptYPosChanged )
        y = geom.y();
    if( acceptWidthChanged )
        w = geom.width();
    if( acceptHeightChanged )
        h = geom.height();

    //change geometry
    setGeometry( x, y, w, h );

    //make sure future signals get sent out properly
    emitGeometryChangedSignal = true;
}

void VisBaseElement::xPosChangedSlot(float xPos)
{
    if( !acceptXPosChanged )
        return;
    if( xPos == geometry().x() )
        return;

    //make sure we don't get infinite signals of geometry changed
    emitGeometryChangedSignal = false;

    //change geometry
    setGeometry( xPos, geometry().y(), geometry().width(), geometry().height() );

    //make sure future signals get sent out properly
    emitGeometryChangedSignal = true;

     //update();
}

void VisBaseElement::yPosChangedSlot(float yPos)
{
    if( !acceptYPosChanged )
        return;
    if( yPos == geometry().y() )
        return;

    //make sure we don't get infinite signals of geometry changed
    emitGeometryChangedSignal = false;

    //change geometry
    setGeometry( geometry().x(), yPos, geometry().width(), geometry().height() );

    //make sure future signals get sent out properly
    emitGeometryChangedSignal = true;

    //update();
}

void VisBaseElement::widthChangedSlot(float width)
{
    if( !acceptWidthChanged )
        return;
    if( width == geometry().width() )
        return;

    //make sure we don't get infinite signals of width changed
    emitWidthChangedSignal = false;

    //change width
    setWidth( width );

    //make sure future signals get sent out properly
    emitWidthChangedSignal = true;

    //update();
}

void VisBaseElement::heightChangedSlot(float height)
{
    if( !acceptHeightChanged )
        return;
    if( height == geometry().height() )
        return;

    //make sure we don't get infinite signals of height changed
    emitHeightChangedSignal = false;

    //change height
    setHeight( height );

    //make sure future signals get sent out properly
    emitHeightChangedSignal = true;

    //update();
}

//This is called when a connected element is hovered over
void VisBaseElement::hoverConnectionSlot(QGraphicsSceneHoverEvent *event)
{
    //make sure we don't get infinite signals of hovering changed
    emitHoverSignal = false;

    //processHoverEvent(event);
    receiveHoverEvent(event); //switching to a new function, to distinguish between recieving a hover event yourself and getting it from another object

    //make sure future signals get sent out properly
    emitHoverSignal = true;

    //update();
}

//This is called when a connected element is hovered over
void VisBaseElement::updatedSlot()
{
    //make sure we don't get infinite signals of hovering changed
    emitUpdatedSignal = false;

    //processHoverEvent(event);
    update(); //switching to a new function, to distinguish between recieving a hover event yourself and getting it from another object

    //make sure future signals get sent out properly
    emitUpdatedSignal = true;
}

void VisBaseElement::colorChangedSlot(QColor c)
{
    if( !acceptColorChangedSignals )
        return;

    emitColorChangedSignal = false;

    processColorChangeEvent( c );

    emitColorChangedSignal = true;
}

void VisBaseElement::processColorChangeEvent(QColor newColor)
{
    if( emitColorChangedSignal )
         emit( colorChangedSignal(newColor) );

    //then do nothing- must reimplement

}

//This is called by scene when user hovers over this object
void VisBaseElement::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
    processHoverEvent(event);
}

//This is called by scene when user leaves hovering over this object
void VisBaseElement::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
    processHoverEvent(event);
}


//---------------------------PROCESS SIGNALS AND SLOTS ----------------------

void VisBaseElement::processHoverEvent(QGraphicsSceneHoverEvent *event)
{
    if( boundingRect().contains(event->pos()) )
        hovering = true;
    else
        hovering = false;

   //hovering = !hovering;

   if( emitHoverSignal )
        emit( hoverConnectionSignal(event) );

   update();

    //do nothing else for now
}

void VisBaseElement::receiveHoverEvent(QGraphicsSceneHoverEvent *event)
{
    hovering = !hovering;
    update();
}

void VisBaseElement::update()
{
    if( emitUpdatedSignal )
        emit( updatedSignal() );

    super::update();
}


//-----------------------------WIDTH AND HEIGHT-----------------------------
    //--------FLOAT---------------------------------------------------------
void VisBaseElement::setWidth(float w)
{
    if( emitWidthChangedSignal )
    {
        emit( widthChangedSignal(w) );
    }

    //make sure we don't get infinite signals of geometry changed
    emitGeometryChangedSignal = false;

    setGeometry( geometry().x(), geometry().y(), w, geometry().height());

    //make sure future signals get sent out properly
    emitGeometryChangedSignal = true;
}

void VisBaseElement::setHeight(float h)
{
    //qDebug() << "height changed " << endl;
    if( emitHeightChangedSignal )
    {
        emit( heightChangedSignal(h) );
    }

    //make sure we don't get infinite signals of geometry changed
    emitGeometryChangedSignal = false;

    setGeometry( geometry().x(), geometry().y(), geometry().width(), h);

    //make sure future signals get sent out properly
    emitGeometryChangedSignal = true;
}

     //-------INT------------------------------------------------------------
//void VisBaseElement::setWidth(int w)
//{
//    setWidth( (float) w );
//}

//void VisBaseElement::setHeight(int h)
//{
//    setHeight( (float) h );
//}

//-------------------------------SIZE AND SHAPE-----------------------------------
void VisBaseElement::setGeometry(const QRectF &geom)
{
    if( emitGeometryChangedSignal )
    {
        emit( geometryChangedSignal(geom) );
    }

    prepareGeometryChange();
    QGraphicsWidget::setGeometry(geom);
    //setPos(geom.topLeft());

    resetLineConnectionPoint();

    emit( updatedSignal() );//needed to update line connection
                            //could also call update, but I think this is done by prepareGeometryChange()- calls QGraphicsWidget update

}

//TO DO:  test all of these connection point types
void VisBaseElement::resetLineConnectionPoint()
{
    if( lineConnectionType == CENTER_LINE_CONNECTION )
    {
        lineConnectionPoint.setX(boundingRect().center().x());
        lineConnectionPoint.setY(boundingRect().center().y());
    }
    if( lineConnectionType == CENTER_RIGHT_LINE_CONNECTION )
    {
        lineConnectionPoint.setX(boundingRect().right() );
        lineConnectionPoint.setY(boundingRect().center().y());
    }
    if( lineConnectionType == CENTER_LEFT_LINE_CONNECTION )
    {
        lineConnectionPoint.setX(boundingRect().left() );
        lineConnectionPoint.setY(boundingRect().center().y());
    }
    if( lineConnectionType == TOP_LEFT_LINE_CONNECTION )
    {
        lineConnectionPoint.setX(boundingRect().topLeft().x() );
        lineConnectionPoint.setY(boundingRect().topLeft().y() );
    }
    if( lineConnectionType == TOP_RIGHT_LINE_CONNECTION )
    {
        lineConnectionPoint.setX(boundingRect().topRight().x() );
        lineConnectionPoint.setY(boundingRect().topRight().y() );
    }
    if( lineConnectionType == BOTTOM_RIGHT_LINE_CONNECTION )
    {
        lineConnectionPoint.setX(boundingRect().bottomRight().x() );
        lineConnectionPoint.setY(boundingRect().bottomRight().y() );
    }
    if( lineConnectionType == BOTTOM_LEFT_LINE_CONNECTION )
    {
        lineConnectionPoint.setX(boundingRect().bottomLeft().x() );
        lineConnectionPoint.setY(boundingRect().bottomLeft().y() );
    }
    if( lineConnectionType == BOTTOM_CENTER_LINE_CONNECTION )
    {
        lineConnectionPoint.setX(boundingRect().center().x() );
        lineConnectionPoint.setY(boundingRect().bottomLeft().y() );
    }
    if( lineConnectionType == TOP_CENTER_LINE_CONNECTION )
    {
        lineConnectionPoint.setX(boundingRect().center().x() );
        lineConnectionPoint.setY(boundingRect().topLeft().y() );
    }
    if( lineConnectionType == RELATIVE_COORDINATE_LINE_CONNECTION )
    {
        qreal newX = ( lineConnectionPoint.x() - boundingRect().x() )/ boundingRect().width() + boundingRect().x();
        qreal newY = ( lineConnectionPoint.y() - boundingRect().y() )/ boundingRect().height() + boundingRect().y();
        lineConnectionPoint.setX( newX );
        lineConnectionPoint.setY( newY );
    }
}

void VisBaseElement::setLineConnectionType(int type)
{
   lineConnectionType = type;
}

void VisBaseElement::setGeometry(qreal x, qreal y, qreal w, qreal h)
{
    setGeometry(QRect(x, y, w, h));
}

QRectF VisBaseElement::boundingRect() const
{
    return QRectF(QPointF(0,0), geometry().size());
}

QPainterPath VisBaseElement::shape() const
{
    QPainterPath path;
    path.addRect(boundingRect());
    return path;
}

//-----------------------------------GETTERS------------------------------------------
QPointF VisBaseElement::getLineConnectionPoint() const
{
//    QPoint p( boundingRect().x(), boundingRect().y()+boundingRect().height()/2.0 );
//    return mapToScene(p);
    if( !parentWidget )
        qDebug() << "not parent widget " << endl;
//    if( !lineConnectionPoint )
//        qDebug() << "not line connection point " << endl;
    return mapToItem(parentWidget, lineConnectionPoint); //mapped to scene because if returned just the point
}

QPointF VisBaseElement::getLineConnectionPoint(int type) const
{
    QPointF pt;

    if( type == CENTER_LINE_CONNECTION )
    {
        pt.setX(boundingRect().center().x());
        pt.setY(boundingRect().center().y());
    }
    if( type == CENTER_RIGHT_LINE_CONNECTION )
    {
        pt.setX(boundingRect().right() );
        pt.setY(boundingRect().center().y());
    }
    if( type == CENTER_LEFT_LINE_CONNECTION )
    {
        pt.setX(boundingRect().left() );
        pt.setY(boundingRect().center().y());
    }
    if( type == TOP_LEFT_LINE_CONNECTION )
    {
        pt.setX(boundingRect().topLeft().x() );
        pt.setY(boundingRect().topLeft().y() );
    }
    if( type == TOP_RIGHT_LINE_CONNECTION )
    {
        pt.setX(boundingRect().topRight().x() );
        pt.setY(boundingRect().topRight().y() );
    }
    if( type == BOTTOM_RIGHT_LINE_CONNECTION )
    {
        pt.setX(boundingRect().bottomRight().x() );
        pt.setY(boundingRect().bottomRight().y() );
    }
    if( type == BOTTOM_LEFT_LINE_CONNECTION )
    {
        pt.setX(boundingRect().bottomLeft().x() );
        pt.setY(boundingRect().bottomLeft().y() );
    }
    if( type == BOTTOM_CENTER_LINE_CONNECTION )
    {
        pt.setX(boundingRect().center().x() );
        pt.setY(boundingRect().bottomLeft().y() );
    }
    if( type == TOP_CENTER_LINE_CONNECTION )
    {
        pt.setX(boundingRect().center().x() );
        pt.setY(boundingRect().topLeft().y() );
    }
    if( type == RELATIVE_COORDINATE_LINE_CONNECTION )
    {
        qreal newX = ( lineConnectionPoint.x() - boundingRect().x() )/ boundingRect().width() + boundingRect().x();
        qreal newY = ( lineConnectionPoint.y() - boundingRect().y() )/ boundingRect().height() + boundingRect().y();
        pt.setX( newX );
        pt.setY( newY );
    }

    return pt;
}

QString VisBaseElement::getUniqueId()
{
    return "";//TO DO:  some way to have good unique id for any elt?
}
